#include <QtCore/QCoreApplication>
#include <QPluginLoader>
#include <QDir>
#include <QFile>

#include <math.h>

#include "../plugin.h"
#include "../data/fileplugin.h"
#include "../data/reg3dvolumedata.h"
#include "../contree/contreeconstructor3d.h"
#include "../contree/augmentedctree.h"
#include "../contree/ctreecontourextractor.h"
#include "../math/timer.h"
#include "../mact/dctconstructor.h"
#include "../mact/dualgraph.h"
#include "../shape/sphere.h"
#include "../math/BSplineInterpol.h"

using namespace PBVP;

int main(int argc, char *argv[])
{

	QCoreApplication a(argc, argv);
	//return a.exec();

	// load plugins
	QDir pluginsDir = QDir(a.applicationDirPath());
	std::cout << pluginsDir.absolutePath().toStdString() << std::endl;
	if (pluginsDir.dirName().toLower() == "debug"
		|| pluginsDir.dirName().toLower() == "release") {
			pluginsDir.cdUp();
	}
	// pluginsDir.cdUp();
	pluginsDir.cd("plugins");

	QStringList pluginEntries = pluginsDir.entryList(QDir::Files);

	FilePluginInterface* rawivPlugin;
	FilePluginInterface* rawPlugin;
	FilePluginInterface* rawncPlugin;

	// check additional potential plugin path if necessary
	if(pluginEntries.size() == 0) {
		pluginsDir = QDir(a.applicationDirPath());
		pluginsDir.cd("plugins");
		pluginEntries = pluginsDir.entryList(QDir::Files);
	}

	foreach (QString fileName, pluginEntries) {
		if (fileName.contains("rawiv"))
		{
			QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
			QObject *plugin = loader.instance();
			if (plugin) {
				rawivPlugin = qobject_cast<FilePluginInterface *>(plugin);
				if (rawivPlugin) {
					rawivPlugin->initialize(NULL);
					std::cout << rawivPlugin->description().toStdString() << std::endl;
				}
			}
		} else if (fileName.contains("rawfile"))
		{
			QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
			QObject *plugin = loader.instance();
			if (plugin) {
				rawPlugin = qobject_cast<FilePluginInterface *>(plugin);
				if (rawPlugin) {
					rawPlugin->initialize(NULL);
					std::cout << rawPlugin->description().toStdString() << std::endl;
				}
			}
		} else if (fileName.contains("rawncfile"))
		{
			QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
			QObject *plugin = loader.instance();
			if (plugin)
			{
				rawncPlugin = qobject_cast<FilePluginInterface *>(plugin);
				if (rawncPlugin)
				{
					rawncPlugin->initialize(NULL);
					std::cout << rawncPlugin->description().toStdString() << std::endl;
				}
			}
		}
	}

	unsigned int dim[3] = {64, 64, 64};
	float orig[3] = {0, 0, 0};
	float span[3] = {1, 1, 1};
	Reg3DVolumeData *pReg1 = new Reg3DVolumeData(dim);
	float *data = new float[dim[0]*dim[1]*dim[2]];
	float *coef = new float[dim[0]*dim[1]*dim[2]];
	for(int k = 0; k < dim[2]; k++) {
		for (int j = 0; j < dim[1]; j++ ) {
			for (int i = 0; i < dim[0]; i++)
			{
				// a sphere centered at (32, 32, 32)
				data[k*dim[1]*dim[0]+j*dim[0]+i] = sqrt((i-32.0)*(i-32.0) + (j-32)*(j-32) + (k-32)*(k-32));
				coef[k*dim[1]*dim[0]+j*dim[0]+i] = data[k*dim[1]*dim[0]+j*dim[0]+i];
			}
		}
	}
	pReg1->setNumOfVariables(1);
	pReg1->setOrigin(orig);
	pReg1->setSpan(span);
	pReg1->setDataArray(data);
	pReg1->calcGradient();

	BSplineInterpol::computeTensor();
	BSplineInterpol::interpolCoeff3D(coef, dim[0], dim[1], dim[2], 1.0e-11);
	float par[10];
	for (unsigned int i = 0; i < dim[0]; i++)
	{
		BSplineInterpol::partialDeriv12(coef, 1.0f, 1.0f, 1.0f, dim[0], dim[1], dim[2], i, i, i, par);
		double grad[3], par2[6];
		pReg1->getVertGrad(i, i, i, grad);
		pReg1->getVertParDeriv2(i, i, i, par2);
		printf("%d %f -- %f, %f, %f -- %f\n", i, data[i*dim[1]*dim[0] + i*dim[0]+i], grad[0], grad[1], grad[2], par2[0]+par2[3]+par2[5]);
		printf("   %d %f -- %f, %f, %f\ -- %f \n", i, par[0], par[1], par[2], par[3], par[4]+par[7]+par[9]);
		double minus[3], plus[3];
		pReg1->getQuadENODerivatives(i, i, i, plus, minus);
		printf("   %d %f -- %f, %f, %f\n", i, pReg1->getValue(i, i, i), minus[0], minus[1], minus[2]);
		printf("   %d %f -- %f, %f, %f\n", i, pReg1->getValue(i, i, i), plus[0], plus[1], plus[2]);
	}

	// rawivPlugin->saveFile(pReg1, "sphere.rawiv");
	delete pReg1;
}
